﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using System.Web;
using RIABlog.Web.Helpers;
using RIABlog.Web.Models;
using RIABlog.Web.Resources;

namespace RIABlog.Web
{
	/// <summary>
	/// Серверный контекст доступа к данным
	/// </summary>
    [EnableClientAccess]
    public class RIABlogService : DomainService
    {
        #region Blog CRUD methods

        /// <summary>
        /// Выдает список всех блогов
        /// </summary>
        public IEnumerable<Blog> GetBlogs()
        {
            // получаем все папки с блогами
            return
                from 
                    authorFolder in Directory.EnumerateDirectories(BaseFolder)
                from 
					blogFolder in Directory.EnumerateDirectories(authorFolder)
                let
                    folderInfo = new DirectoryInfo(blogFolder)
                let 
                    titleAndId = folderInfo.Name.Split(NameSeparator) 
                where 
                    titleAndId.Length > 1
                select 
                    new Blog
                        {
							Id = Guid.Parse(titleAndId[1]),
							Title = titleAndId[0],
							Author = folderInfo.Parent == null ? string.Empty : folderInfo.Parent.Name.FromValidPath(),
                            TimeCreated = folderInfo.CreationTime,
                            TimeLastPosted = folderInfo.LastWriteTime
                        };
        }

        /// <summary>
        /// Создает новый блог. Доступен только аутентифицированным пользователям.
        /// </summary>
        [RequiresAuthentication]
        public void CreateBlog(Blog newBlog)
        {
            // Задаем сущности новый идентификатор. Он автоматически попадет на клиента.
            newBlog.Id = Guid.NewGuid();

            // Получаем имя текущего аутентифицированного пользователя
            // из ASP.Net-контекста. Значение этого свойства тоже автоматически попадет на клиента.
            newBlog.Author = HttpContext.Current.User.Identity.Name;

            newBlog.TimeCreated = newBlog.TimeLastPosted = DateTime.Now;

            // создаем папку блога
            Directory.CreateDirectory(newBlog.GetNewFolder());
        }

        /// <summary>
        /// Обновляет существующий блог. Доступен только аутентифицированным 
        /// пользователям.
        /// </summary>
        [RequiresAuthentication]
        public void UpdateBlog(Blog updatedBlog)
        {
            // проверяем автора
            if (updatedBlog.Author != HttpContext.Current.User.Identity.Name)
            {
                throw new SecurityException(ErrorMessages.AccessToBlogDenied);
            }

            // Переименовываем папку блога
            Directory.Move(updatedBlog.GetExistingFolder(), updatedBlog.GetNewFolder());
        }
		
		/// <summary>
        /// Удаляет блог. Доступен только аутентифицированным пользователям.
        /// </summary>
        [RequiresAuthentication]
        public void DeleteBlog(Blog removedBlog)
        {
            // проверяем автора
            if (removedBlog.Author != HttpContext.Current.User.Identity.Name)
            {
                throw new SecurityException(ErrorMessages.AccessToBlogDenied);
            }

            // Удаляем папку блога
            Directory.Delete(removedBlog.GetExistingFolder(), true);
        }

        #endregion

        #region BlogPost CRUD methods

	    /// <summary>
		/// Выдает записи данного блога
		/// </summary>
		public IEnumerable<BlogPost> GetBlogPosts(Guid blogId)
		{
			string blogFolder = EntityExtensions.GetExistingBlogFolder(blogId);

			// получаем все папки с постами данного блога, отсортированные по дате создания
			foreach
			(
				var postInfo in 
					from 
						postFolder in Directory.EnumerateDirectories(blogFolder) 
					let 
						folderInfo = new DirectoryInfo(postFolder)
					let 
						titleAndId = folderInfo.Name.Split(NameSeparator) 
					where 
						titleAndId.Length > 1 
                    orderby 
                        folderInfo.CreationTime descending 
					select new
					    {
					        postFolder, 
						    postId = new Guid(titleAndId[1]),
						    postTitle = titleAndId[0],
						    postTimePosted = folderInfo.CreationTime
					    }
			)
			{
				// создаем экземпляр сущности поста
				var post = new BlogPost
					{
						BlogId = blogId,
						Id = postInfo.postId,
						Title = postInfo.postTitle,
						TimePosted = postInfo.postTimePosted
					};

				// пытаемся прочитать текст поста и ключевые слова из файла
				string fileName = Directory.EnumerateFiles(postInfo.postFolder, "*" + PostFileExtension).FirstOrDefault();
				if (!string.IsNullOrEmpty(fileName))
				{
					// ключевые слова
					post.Keywords = EntityExtensions.ExtractKeywordsFromFileName(fileName).ToArray();

                    // текст
					using (var reader = new StreamReader(fileName))
					{
						post.Text = reader.ReadToEnd();
					}
				}

				// получаем комментарии
			    var comments = new List<Comment>();

			    foreach
                (
                    var commentInfo in
                        from
                            commentFileFullName in Directory.EnumerateFiles(postInfo.postFolder, "*" + CommentFileExtension)
                        let
                            fileInfo = new FileInfo(commentFileFullName)
                        let
                            commentFileName = Path.GetFileNameWithoutExtension(fileInfo.Name)
                        let
                            authorAndId = commentFileName.Split(NameSeparator)
                        where
                            authorAndId.Length > 1
                        orderby
                            fileInfo.CreationTime 
			            select new
			                {
                                commentFileFullName,
			                    commentId = new Guid(authorAndId[1]), 
                                commentAuthor = authorAndId[0].FromValidPath(),
                                commentTimeCreated = fileInfo.CreationTime
			                }
                )
			    {
                    // создаем экземпляр сущности комментария
			        var comment = new Comment
			            {
                            Id = commentInfo.commentId,
                            PostId = post.Id,
                            Author = commentInfo.commentAuthor,
                            TimePosted = commentInfo.commentTimeCreated
			            };

                    // читаем текст комментария
                    using (var reader = new StreamReader(commentInfo.commentFileFullName))
                    {
                        comment.Text = reader.ReadToEnd();
                    }

                    comments.Add(comment);
                }

			    post.Comments = comments;

			    yield return post;
			}
		}

    	/// <summary>
		/// Создает новый пост. Доступен только аутентифицированным пользователям.
		/// </summary>
		[RequiresAuthentication]
		public void CreateBlogPost(BlogPost newPost)
		{
			// проверяем автора
			if (newPost.GetBlogAuthor() != HttpContext.Current.User.Identity.Name)
			{
				throw new SecurityException(ErrorMessages.AccessToBlogDenied);
			}

			// Задаем сущности новый идентификатор. Он автоматически попадет на клиента.
			newPost.Id = Guid.NewGuid();

			newPost.TimePosted = DateTime.Now;

			// Создаем папку поста
    		Directory.CreateDirectory(newPost.GetNewFolder());

			// Пишем текст в файл
			using (var writer = new StreamWriter(newPost.GetFileName()))
			{
				writer.Write(newPost.Text);
			}
		}

		/// <summary>
        /// Удаляет пост. Доступен только аутентифицированным пользователям.
		/// </summary>
        /// <param name="removedPost"></param>
		[RequiresAuthentication]
		public void DeleteBlogPost(BlogPost removedPost)
		{
            // проверяем автора
            if (removedPost.GetBlogAuthor() != HttpContext.Current.User.Identity.Name)
            {
                throw new SecurityException(ErrorMessages.AccessToBlogDenied);
            }

            // Удаляем папку поста
            Directory.Delete(removedPost.GetExistingFolder(), true);
        }

	    #endregion

		#region Comment CRUD methods

    	/// <summary>
		/// Создает новый комментарий. Доступен только аутентифицированным пользователям.
		/// </summary>
		[RequiresAuthentication]
		public void CreateComment(Comment newComment)
    	{
			// Задаем сущности новый идентификатор. Он автоматически попадет на клиента.
			newComment.Id = Guid.NewGuid();

			// Получаем имя текущего аутентифицированного пользователя
			// из ASP.Net-контекста. Значение этого свойства тоже автоматически попадет на клиента.
			newComment.Author = HttpContext.Current.User.Identity.Name;

			newComment.TimePosted = DateTime.Now;

            // Пишем текст в файл
            using (var writer = new StreamWriter(newComment.GetFileName()))
            {
                writer.Write(newComment.Text);
            }
        }

    	#endregion

		#region Command methods

		/// <summary>
		/// Выдает список команд
		/// </summary>
		public IEnumerable<BaseCommand> GetCommands()
		{
            yield return new AgreeCommand();
            yield return new DisagreeCommand();
            yield return new ModerateCommand();
		}

		/// <summary>
		/// Выполняет команду
		/// </summary>
		/// <param name="command"></param>
		/// <param name="entityId"></param>
		/// <returns></returns>
		[Invoke]
		public string ExecuteCommand(BaseCommand command, Guid entityId)
		{
			// передаем выполнение самому объекту команды
			return command.Execute(entityId);
		}

		#endregion

		#region Private Properties

		/// <summary>
		/// Путь к папке со всеми блогами
		/// </summary>
		private static string BaseFolder{ get { return EntityExtensions.BaseFolder; } }

        /// <summary>
        /// Разделитель в имени папки блога или поста
        /// </summary>
		private static char NameSeparator { get { return EntityExtensions.NameSeparator; } }

		/// <summary>
		/// Расширение файла с текстом поста
		/// </summary>
		private static string PostFileExtension { get { return EntityExtensions.PostFileExtension; } }

        /// <summary>
        /// Расширение файла с комментарием
        /// </summary>
        private static string CommentFileExtension { get { return EntityExtensions.CommentFileExtension; } }

		#endregion
    }
}